package cfca.paperless.client.servlet._3_assist._3_14_ApplyAndDownloadCert;

import java.security.PrivateKey;

import cfca.paperless.client.servlet.PaperlessClient;
import cfca.paperless.client.servlet.PaperlessConfig;
import cfca.paperless.client.util.Base64;
import cfca.paperless.client.util.IoUtil;
import cfca.paperless.client.util.StringUtil;
import cfca.sadk.algorithm.common.Mechanism;
import cfca.sadk.asn1.pkcs.PKCS12_SM2;
import cfca.sadk.lib.crypto.JCrypto;
import cfca.sadk.lib.crypto.Session;
import cfca.sadk.org.bouncycastle.asn1.x500.style.CFCAStyle;
import cfca.sadk.util.P10Request;
import cfca.sadk.util.Signature;
import cfca.sadk.x509.certificate.X509Cert;

/**
 * @Description 申请并下载证书接口，使用示例。SM2。
 * 
 * @Author dell
 * @Date 2018-1-24
 * @CodeReviewer TODO
 */
public class ApplyAndDownloadCertTest02 {

    public static void main(String[] args) throws Exception {

        PaperlessClient clientBean = new PaperlessClient(PaperlessConfig.assistUrl, 10000, 60000);

        String customerType = "1";// 客户类型，1：个人普通；2：企业普通；7：个人场景证书
        String certLevel = "2";// 证书类型，1：普通；2：高级

        String keyAlg = Mechanism.SM2;// 密钥算法：RSA，SM2（只能是高级证书类型）
        int keyLength = 256;// 密钥长度：1024，2048, 256
        String signAlg = Mechanism.SM3_SM2;// 签名算法

        String userName = "小张飞";// 用户名
        String identificationType = "0";// 证件类型
        String identificationNo = "110120201712301234";// 证件号码

        String selfExtValue = "";// 自定义扩展域值（场景证书时使用）

        String invokeWay = "1";// 调用方式，1：local；2：remote，无需连接云平台的话，就固定设置为1

        String p10String = "";
        PrivateKey privateKey = null;
        String password = "11111111";

        try {
            JCrypto.getInstance().initialize(JCrypto.JSOFT_LIB, null);
            final Session session = JCrypto.getInstance().openSession(JCrypto.JSOFT_LIB);

            P10Request p10Request = new P10Request(session);
            Mechanism mechanism = new Mechanism(signAlg);
            byte[] p10Data = p10Request.generatePKCS10Request(mechanism, keyLength, session);

            privateKey = p10Request.getPrivateKey();
            p10String = new String(p10Data, "UTF-8");

            String applyCertStrategyXML = getApplyCertStrategyXML(customerType, certLevel, keyAlg, keyLength, userName, identificationType, identificationNo,
                    selfExtValue, p10String, invokeWay);
            System.out.println(applyCertStrategyXML);

            String applyResult = clientBean.applyAndDownloadCert(applyCertStrategyXML, PaperlessConfig.operatorCode, PaperlessConfig.channelCode);

            String code = StringUtil.getNodeText(applyResult, "Code");

            // 返回值Code等于200代表后台处理成功
            if ("200".equals(code)) {

                String certData = StringUtil.getXmlField(applyResult, "Cert");

                X509Cert cert = new X509Cert(Base64.decode(certData.getBytes("UTF-8")));

                boolean verifySign = checkCertMatchKey(privateKey, cert, signAlg);
                if (verifySign) {// 验证证书签名成功
                    System.out.println("OK");
                    System.out.println(applyResult);
                    System.out.println("Issuer=[" + cert.getIssuer() + "]");
                    System.out.println("DN=[" + cert.getSubject(CFCAStyle.INSTANCE) + "]");
                    System.out.println("SerialNumber=[" + cert.getStringSerialNumber() + "]");
                    System.out.println("NotBefore=[" + cert.getNotBefore() + "]");
                    System.out.println("NotAfter=[" + cert.getNotAfter() + "]");

                    IoUtil.write("./TestData/cert/sm2.cer", cert.getEncoded());
                    IoUtil.write("./TestData/cert/sm2.sm2", PKCS12_SM2.generateSM2Data(cert, privateKey, password));

                } else {// 验证证书签名失败
                    System.out.println("NG");
                    System.out.println(applyResult);
                }

            } else {// Code不等于200时为异常
                System.out.println("NG");
                System.out.println(applyResult);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static String getApplyCertStrategyXML(String customerType, String certLevel, String keyAlg, int keyLength, String userName,
            String identificationType, String identificationNo, String selfExtValue, String p10String, String invokeWay) throws Exception {

        StringBuilder applyCertStrategyXML = new StringBuilder("<Request>");

        applyCertStrategyXML.append("<CustomerType>").append(customerType).append("</CustomerType>");// 客户类型，1：个人普通；2：企业普通；7：个人场景证书
        applyCertStrategyXML.append("<CertLevel>").append(certLevel).append("</CertLevel>");// 证书类型，1：普通；2：高级

        applyCertStrategyXML.append("<KeyAlg>").append(keyAlg).append("</KeyAlg>");// 密钥算法：RSA，SM2（只能是高级）
        applyCertStrategyXML.append("<KeyLength>").append(keyLength).append("</KeyLength>");// 密钥长度：1024，2048，256

        applyCertStrategyXML.append("<UserName>").append(userName).append("</UserName>");// 用户名
        applyCertStrategyXML.append("<IdentificationType>").append(identificationType).append("</IdentificationType>");// 证件类型
        applyCertStrategyXML.append("<IdentificationNo>").append(identificationNo).append("</IdentificationNo>");// 证件号码

        applyCertStrategyXML.append("<SelfExtValue>").append(selfExtValue).append("</SelfExtValue>");// 自定义扩展域值（场景证书时使用）

        applyCertStrategyXML.append("<P10>").append(p10String).append("</P10>");//

        applyCertStrategyXML.append("<InvokeWay>").append(invokeWay).append("</InvokeWay>");//

        applyCertStrategyXML.append("</Request>");

        return applyCertStrategyXML.toString();
    }

    /**
     * 验证证书签名
     * 
     * @param privateKey
     * @param x509Cert
     * @param device
     * @throws CodeException
     * @throws Exception
     */
    private static boolean checkCertMatchKey(PrivateKey privateKey, X509Cert x509Cert, String signAlg) throws Exception {
        byte[] testData = "CFCA".getBytes("UTF-8");

        boolean verifySign = false;
        Signature signature = new Signature();
        try {
            JCrypto.getInstance().initialize(JCrypto.JSOFT_LIB, null);
            final Session session = JCrypto.getInstance().openSession(JCrypto.JSOFT_LIB);

            byte[] sign = signature.p1SignMessage(signAlg, testData, privateKey, session);
            verifySign = signature.p1VerifyMessage(signAlg, testData, sign, x509Cert.getPublicKey(), session);
        } catch (Exception e) {
            throw e;
        }
        if (!verifySign) {
            throw new Exception("证书数据异常！");
        }
        return verifySign;
    }
}
